package huanzhao.erp.service;

import huanzhao.erp.domain.entity.*;
import huanzhao.erp.domain.po.Result;
import huanzhao.erp.domain.types.*;
import huanzhao.erp.domain.vo.GlobalSettingVO;
import huanzhao.erp.domain.vo.LabelVO;
import huanzhao.erp.mapper.*;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.validator.constraints.Range;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Past;
import java.lang.Exception;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * @author April
 * 2024/10/4 13:08
 * @version 1.0
 */
@Service
@Slf4j
public class MiscService {
    private final GlobalSettingMapper globalSettingMapper;
    private final ConvertService convertService;
    private final StaffMapper staffMapper;
    private final LabelMapper labelMapper;
    private final BasicService basicService;
    private final BillMapper billMapper;
    private final FinanceSettlementMapper financeSettlementMapper;
    private final NotificationService notificationService;
    private final TaskMapper taskMapper;
    private final ExceptionMapper exceptionMapper;
    private final PositionService positionService;
    private final ClearMapper clearMapper;
    private final PhoneMapper phoneMapper;
    private final ClearService clearService;
    private final CommentMapper commentMapper;
    private final ClearRecordMapper clearRecordMapper;
    private final CostMapper costMapper;
    private final PhoneService phoneService;
    private final PhonePublishSoldMapper phonePublishSoldMapper;

    public MiscService(GlobalSettingMapper globalSettingMapper, ConvertService convertService, StaffMapper staffMapper, LabelMapper labelMapper, BasicService basicService, BillMapper billMapper, FinanceSettlementMapper financeSettlementMapper, NotificationService notificationService, TaskMapper taskMapper, ExceptionMapper exceptionMapper, PositionService positionService, ClearMapper clearMapper, PhoneMapper phoneMapper, ClearService clearService, CommentMapper commentMapper, ClearRecordMapper clearRecordMapper, CostMapper costMapper, @Lazy PhoneService phoneService, PhonePublishSoldMapper phonePublishSoldMapper) {
        this.globalSettingMapper = globalSettingMapper;
        this.convertService = convertService;
        this.staffMapper = staffMapper;
        this.labelMapper = labelMapper;
        this.basicService = basicService;
        this.billMapper = billMapper;
        this.financeSettlementMapper = financeSettlementMapper;
        this.notificationService = notificationService;
        this.taskMapper = taskMapper;
        this.exceptionMapper = exceptionMapper;
        this.positionService = positionService;
        this.clearMapper = clearMapper;
        this.phoneMapper = phoneMapper;
        this.clearService = clearService;
        this.commentMapper = commentMapper;
        this.clearRecordMapper = clearRecordMapper;
        this.costMapper = costMapper;
        this.phoneService = phoneService;
        this.phonePublishSoldMapper = phonePublishSoldMapper;
    }

    /**
     * 获取全局配置
     *
     * @return GlobalConfigVO
     * 2024/10/4 11:45:11
     */
    public GlobalSettingVO getGlobalSetting() {
        GlobalSetting globalSetting = globalSettingMapper.selectById(1);
        if (globalSetting == null) {
            return null;
        }
        return convertService.convertGlobalSetting(globalSetting);
    }

    /**
     * 检查全局设置
     *
     * @param globalSetting 全局设置
     * @return Result<?>
     * 2024/10/4 12:56:16
     */
    public Result<?> checkGlobalSetting(GlobalSetting globalSetting) {
        if (globalSetting == null) {
            return Result.error("全局设置不能为空");
        }
        if (globalSetting.getLimit() == null || globalSetting.getLimit() <= 0) {
            return Result.error("接收时限不合法");
        }
        if (globalSetting.getManagerId() == null || globalSetting.getManagerId() <= 0) {
            return Result.error("业务经理id不合法");
        }
        if (!staffMapper.exist(globalSetting.getManagerId())) {
            return Result.error("业务经理不存在");
        }
        if (globalSetting.getRepairLeaderId() == null || globalSetting.getRepairLeaderId() <= 0) {
            return Result.error("维修组长id不合法");
        }
        if (!staffMapper.exist(globalSetting.getRepairLeaderId())) {
            return Result.error("维修组长不存在");
        }
        if (globalSetting.getWarehouseHandlerId() == null || globalSetting.getWarehouseHandlerId() <= 0) {
            return Result.error("仓库异常处理人id不合法");
        }
        if (!staffMapper.exist(globalSetting.getWarehouseHandlerId())) {
            return Result.error("仓库异常处理人不存在");
        }
        if (globalSetting.getFinancerId() == null || globalSetting.getFinancerId() <= 0) {
            return Result.error("财务员id不合法");
        }
        if (!staffMapper.exist(globalSetting.getFinancerId())) {
            return Result.error("财务员不存在");
        }
        if (globalSetting.getPurchaseHandlerId() == null || globalSetting.getPurchaseHandlerId() <= 0) {
            return Result.error("采购异常处理人id不合法");
        }
        if (!staffMapper.exist(globalSetting.getPurchaseHandlerId())) {
            return Result.error("采购异常处理人不存在");
        }
        return Result.ok();
    }

    /**
     * 修改全局设置
     *
     * @param globalSetting 全局设置
     * @return Result<?>
     * 2024/10/4 12:57:53
     */
    public Result<?> updateGlobalSetting(GlobalSetting globalSetting) {
        try {
            globalSetting.setId(1L);
            globalSettingMapper.updateById(globalSetting);
            return Result.ok("修改全局设置成功");
        } catch (Exception e) {
            log.error("修改全局设置失败", e);
            return Result.error("其他错误，请联系开发人员处理");
        }
    }

    /**
     * 查询所有标签
     *
     * @return List<LabelVO>
     * 2024/10/4 13:12:18
     */
    public List<LabelVO> getAllLabels() {
        List<Label> labels = labelMapper.selectList(null);
        if (labels == null) {
            return Collections.emptyList();
        }
        List<LabelVO> result = new ArrayList<>();
        labels.forEach(label -> result.add(convertService.convertLabel(label)));
        return result;
    }

    /**
     * 按id查询标签
     *
     * @param labelId 标签id
     * @return LabelVO
     * 2024/10/4 13:15:16
     */
    public LabelVO getLabelById(long labelId) {
        Label label = labelMapper.selectById(labelId);
        if (label == null) {
            return null;
        }
        return convertService.convertLabel(label);
    }

    /**
     * 添加标签
     *
     * @return Result<?>
     * 2024/10/4 13:46:17
     */
    public Result<?> addLabel(Label label) {
        try {
            label.setModifierId(basicService.getCurrentStaffId());
            labelMapper.insert(label);
            return Result.ok("添加标签成功");
        } catch (Exception e) {
            log.error("添加标签失败", e);
            return Result.error("添加标签失败");
        }
    }

    /**
     * 修改标签
     *
     * @return Result<?>
     * 2024/10/4 13:47:38
     */
    public Result<?> updateLabel(Label label) {
        if (label == null) {
            return Result.error("标签不能为空");
        }
        if (label.getId() == null) {
            return Result.error("标签id不能为空");
        }
        if (labelMapper.selectById(label.getId()) == null) {
            return Result.error("标签不存在");
        }
        try {
            label.setModifierId(basicService.getCurrentStaffId());
            label.setLastModified(new Date());
            labelMapper.updateById(label);
            return Result.ok("修改标签成功");
        } catch (Exception e) {
            log.error("修改标签失败", e);
            return Result.error("修改标签失败");
        }
    }

    /**
     * 删除标签
     *
     * @return Result<?>
     * 2024/10/4 13:52:33
     */
    public Result<?> deleteLabelById(long labelId) {
        if (labelMapper.selectById(labelId) == null) {
            return Result.error("标签不存在");
        }
        try {
            labelMapper.deleteById(labelId);
            return Result.ok("删除标签成功");
        } catch (Exception e) {
            log.error("删除标签失败", e);
            return Result.error("删除标签失败");
        }
    }

    /**
     * 添加财务记录
     * 包括添加财务结算记录的同时添加收支情况
     *
     * @param phoneId 需要结算的手机id
     * @param price   结算价格
     * @param type    结算类型
     * @param reason  说明原因
     *                2024/10/11 11:08:13
     */
    public void addFinanceRecord(long phoneId, @NotNull BigDecimal price, @NotNull FinanceSettlementType type, String reason) {
        // 添加财务清算信息
        financeSettlementMapper.insert(new FinanceSettlement(phoneId, type, price, reason));
        notificationService.sendNotification(basicService.getGlobalSetting().getFinancerId(), NotificationType.FINANCE_SETTLE, "有一笔" + type.getValue() + "（金额：" + price + "）需要进行财务清算。", phoneId);
    }

    /**
     * 撤销手机的财务记录
     *
     * @param phoneId        手机id
     * @param settlementType 结算类型
     *                       2024/10/13 19:37:38
     */
    public void revokeFinanceRecord(Long phoneId, FinanceSettlementType settlementType) {
        // 尝试撤销结算
        boolean revoked = financeSettlementMapper.revoke(phoneId, settlementType);
        // 若撤销成功，则发送通知给财务员
        if (revoked) {
            notificationService.sendNotification(basicService.getGlobalSetting().getFinancerId(), NotificationType.FINANCE_SETTLE, "有一笔" + settlementType.getValue() + "已被撤销。", phoneId);
        }
    }

    /**
     * 添加异常，并给接收人发送通知
     * 默认异常事务发起人为自己
     *
     * @param receiverId 接收人id
     * @param taskId     相关任务id
     * @param type       异常类型
     * @param comment    备注
     * @param lostTime   丢失时间（当异常类型为丢失时生效）
     *                   2024/10/12 16:58:03
     */
    public void sendException(long receiverId, long taskId, ExceptionType type, String comment, Date lostTime) {
        exceptionMapper.insert(new huanzhao.erp.domain.entity.Exception(basicService.getCurrentStaffId(), receiverId, taskId, type, comment, lostTime));
        // 给业务经理发送通知
        notificationService.sendNotification(receiverId, NotificationType.NEW_EXCEPTION, "您收到一条新的待处理的" + type.getValue() + "异常事务，请及时处理！", taskId);
    }

    /**
     * 报告异常
     * <p>
     * 异常类型为损坏时，交给直属上级，直属上级可以选择维修或忽略。
     * 若选择维修，则由维修组长选择是否报损。
     * 为接收超时时，直属上级只能已读。
     * 为丢失时，交给业务经理审核。
     * 只有维修任务可以选择报损，选择报损后，交给维修组长审核
     *
     * @param type         0->接收超时，1->损坏，2->丢失，3->报损
     * @param reason       超时原因或损坏程度或丢失备注
     * @param discoverTime 发现日期（当为丢失时）
     * @return Result<?>
     * 2024/10/12 14:08:57
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> reportException(long taskId, @Range(max = 3) int type, @NotBlank String reason, @Past Date discoverTime) {
        Task currentTask = taskMapper.selectById(taskId);
        ExceptionType exceptionType = ExceptionType.of(type);
        // 检查是否提交了相同类型的异常事务
        TaskState state = currentTask.getState();
        if (state == TaskState.EXCEPTION) {
            return Result.error("已提交过异常事务，请勿重复提交");
        }
        Long receiverId = null;
        switch (exceptionType) {
            case TIMEOUT: {
                // 检查任务状态是否为待接收，是否为已超时
                if (state != TaskState.WAIT_RECEIVE) {
                    return Result.error("任务状态不合法，应为待接收");
                }
                if (!currentTask.getOvertime()) {
                    return Result.error("接收未超时");
                }
                if (currentTask.getReject()) {
                    return Result.error("已拒绝该任务，无法提交异常");
                }
                // 检查是否发送过异常事务
                boolean submitted = exceptionMapper.existsByTaskAndType(taskId, ExceptionType.TIMEOUT.getValue());
                if (submitted) {
                    return Result.error("已提交过超时异常事务，请勿重复提交");
                }
                // 向直属上级发送异常
                receiverId = positionService.getSuperiorId(currentTask);
                break;
            }
            case LOST:
                if (state != TaskState.PROCESSING && (state != TaskState.WAIT_RECEIVE || !currentTask.getReject())) {
                    return Result.error("任务状态不合法，应为处理中或已拒绝");
                }
                if (discoverTime == null) {
                    return Result.error("丢失时间不能为空");
                }
                receiverId = basicService.getGlobalSetting().getManagerId();
                break;
            case DAMAGE:
                if (state != TaskState.PROCESSING && (state != TaskState.WAIT_RECEIVE || !currentTask.getReject())) {
                    return Result.error("任务状态不合法，应为处理中或已拒绝");
                }
                receiverId = positionService.getSuperiorId(currentTask);
                break;
            case DESTROY:
                if (currentTask.getType() != TaskType.REPAIR) {
                    return Result.error("只有维修任务可以报损");
                }
                if (state != TaskState.PROCESSING) {
                    return Result.error("任务状态不合法，应为处理中");
                }
                receiverId = basicService.getGlobalSetting().getRepairLeaderId();
                break;
        }
        // 向接收人发送异常
        sendException(receiverId, taskId, exceptionType, reason, discoverTime);

        // 若异常不为超时，则将任务状态标记为异常
        if (exceptionType != ExceptionType.TIMEOUT) {
            taskMapper.updateTaskState(taskId, TaskState.EXCEPTION.getValue());
        }
        return Result.ok("提交异常成功");
    }

    /**
     * 处理异常
     * <p>
     * 是否批准，对于超时，只能为是；
     * 对于损坏，是代表送维修，否代表忽略；
     * 对于丢失，是代表确认丢失，否代表找到了；
     * 对于报损，是代表确认报损，否代表忽略报损，确认报损时若当前操作人为维修组长，则继续上报到业务经理，若已经为业务经历，则手机报损。
     * 员工报告损坏、丢失、报损三种情况时，原任务转化到异常状态，当领导处理时选择否，原任务恢复执行状态。
     *
     * @param exceptionId 异常事务id
     * @param approve     是否批准
     * @param response    批复
     * @return Result<?>
     * 2024/10/12 17:27:18
     */
    public Result<?> handleException(long exceptionId, boolean approve, @NotBlank String response) {
        huanzhao.erp.domain.entity.Exception exception = exceptionMapper.selectById(exceptionId);
        if (exception == null) {
            return Result.error("异常事务不存在");
        }
        if (!basicService.getCurrentStaffId().equals(exception.getApproverId())) {
            return Result.error("无权处理该异常事务");
        }
        if (exception.getApprove() != null) {
            return Result.error("该异常事务已处理");
        }
        ExceptionType type = exception.getType();
        switch (type) {
            case TIMEOUT:
                if (!approve) {
                    return Result.error("超时异常只能批准");
                }
                // 发送通知给事务发起人
                notificationService.sendNotification(exception.getApplicantId(), NotificationType.EXCEPTION_REPLY, "您的超时异常已被查看，批复：" + response, exception.getTaskId());
                break;
//            case DAMAGE:
//                if (approve) {
//                    // 送维修
//                    taskMapper.updateTaskState(exception.getTaskId(), TaskState.REPAIR.getValue());
//                } else {
//                    // 忽略
//                    taskMapper.updateTaskState(exception.getTaskId(), TaskState.EXECUTING.getValue());
//                }
//                break;
//            case LOST:
//                if (approve) {
//                    // 确认丢失
//                    taskMapper.updateTaskState(exception.getTaskId(), TaskState.LOST.getValue());
//                } else {
//                    // 找到了
//                    taskMapper.updateTaskState(exception.getTaskId(), TaskState.EXECUTING.getValue());
//                }
//                break;
//            case DESTROY:
//                if (approve) {
//                    // 确认报损
//                    if (basicService.getCurrentStaffId().equals(basicService.getGlobalSetting().getRepairLeaderId())) {
//                        // 继续上报到业务经理
//                        sendException(basicService.getGlobalSetting().getManagerId(), exception.getTaskId(), ExceptionType.DESTROY, "维修组长已确认报损", null);
//                    } else {
//                        // 手机报损
//                        taskMapper.updateTaskState(exception.getTaskId(), TaskState.DESTROY.getValue());
//                    }
//                } else {
//                    // 忽略报损
//                    taskMapper.updateTaskState(exception.getTaskId(), TaskState.EXECUTING.getValue());
//                }
//                break;
        }
        // 完成异常处理
        exceptionMapper.finish(exceptionId, approve, response);
        return Result.ok("处理异常成功");
    }

    /**
     * 确认财务结算数据
     *
     * @param settlementId 结算id
     * @param price        修改结算金额，传输字符串
     * @return Result<?>
     * 2024/10/14 11:02:21
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Result<?> confirmFinanceSettlement(long settlementId, BigDecimal price, String comment) {
        FinanceSettlement settlement = financeSettlementMapper.selectById(settlementId);
        // 校验财务结算数据
        if (settlement == null) {
            return Result.error("结算记录不存在");
        }
        if (settlement.getFinishTime() != null) {
            return Result.error("结算已完成");
        }
        if (settlement.getRevocation()) {
            return Result.error("结算已被撤销");
        }
        if (price == null) {
            price = settlement.getAmount();
        } else if (price.compareTo(BigDecimal.ZERO) <= 0) {
            return Result.error("修改的金额应大于0");
        }

        // 进入结算流程
        FinanceSettlementType type = settlement.getType();

        // 根据结算类型进行相应操作
        final Long phoneId = settlement.getPhoneId();
        switch (type) {
            // 维修成本的情况，添加手机维修成本记录
            case REPAIR_COST -> costMapper.insert(new Cost(phoneId, true, price, comment));
            case SALES_INCOME -> {
                // 销售收入
                // 将手机状态改为已售出
                phoneService.updatePhoneState(phoneId, PhoneState.SOLD, PhoneState.WAIT_SETTLE_WHEN_SALE);
                // 添加手机上架售出记录
                phonePublishSoldMapper.insert(new PhonePublishSold(phoneId, false, price, phoneMapper.getTeamById(phoneId), null));
            }
            //  采购退货和销售退货，无额外操作
        }

        boolean revenue = switch (type) {
            case REPAIR_COST, SALES_RETURN -> false;
            case SALES_INCOME, PURCHASE_RETURN -> true;
        };
        // 添加收支情况
        billMapper.insert(new Bill(revenue, BillType.of(type), price));
        // 完成财务结算
        boolean success = financeSettlementMapper.finishSettlement(settlementId, price, comment);
        if (!success) {
            // 若完成结算失败，则回滚
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            return Result.error("结算失败，结算单状态异常");
        }
        return Result.ok("结算成功");
    }
}
